package com.google.code.stalkernet.example.ui;

import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;

import com.google.code.stalkernet.advertisement.Advertisement;
import com.google.code.stalkernet.advertisements.Advertisements;
import com.google.code.stalkernet.advertisements.event.AdvertisementsEvent;
import com.google.code.stalkernet.advertisements.event.AdvertisementsListener;
import com.google.code.stalkernet.reactor.Reactor;

public class AdvertisementsTableModel
    extends AbstractTableModel implements AdvertisementsListener
{
    private final List <Advertisement> advertisementsList =
        new ArrayList <> ();

    private final Reactor reactor;
    private final Advertisements advertisements;

    public AdvertisementsTableModel (
        Reactor reactor, Advertisements advertisements)
    {
        if (reactor == null)
            throw new IllegalArgumentException ("Reactor is null");

        if (advertisements == null)
            throw new IllegalArgumentException ("Advertisements is null");

        this.reactor = reactor;
        this.advertisements = advertisements;

        reactor.invoke (new DumpAdvertisementsRunnable ());
    }

    public Advertisement getAdvertisementAt (int index)
    {
        return advertisementsList.get (index);
    }

    @Override
    public int getRowCount ()
    {
        return advertisementsList.size ();
    }

    @Override
    public int getColumnCount ()
    {
        return 3;
    }

    @Override
    public Object getValueAt (int rowIndex, int columnIndex)
    {
        Advertisement advertisement = advertisementsList.get (rowIndex);

        InetSocketAddress socketAddress =
            (InetSocketAddress)advertisement.getSocketAddress ();

        switch (columnIndex)
        {
        case 0:
            return socketAddress.getAddress ();
        case 1:
            return socketAddress.getPort ();
        case 2:
            return advertisement.getNodeID ();
        default:
            throw new IllegalArgumentException (
                "Invalid column index: " + columnIndex);
        }
    }

    @Override
    public String getColumnName (int column)
    {
        switch (column)
        {
            case 0:
                return "Address";
            case 1:
                return "Port";
            case 2:
                return "Node ID";
            default:
                throw new IllegalArgumentException (
                    "Invalid column: " + column);
        }
    }

    @Override
    public void advertisementAdded (AdvertisementsEvent advertisementEvent)
    {
        if (advertisementEvent == null)
            throw new IllegalArgumentException ("Advertisement event is null");

        final Advertisement advertisement =
            advertisementEvent.getAdvertisement ();

        SwingUtilities.invokeLater (
            new Runnable ()
            {
                @Override
                public void run ()
                {
                    int index = advertisementsList.size ();
                    advertisementsList.add (index, advertisement);
                    fireTableRowsInserted (index, index);
                }
            });
    }

    @Override
    public void advertisementRemoved (AdvertisementsEvent advertisementEvent)
    {
        if (advertisementEvent == null)
            throw new IllegalArgumentException ("Advertisement event is null");

        final Advertisement advertisement =
            advertisementEvent.getAdvertisement ();

        SwingUtilities.invokeLater (
            new Runnable ()
            {
                @Override
                public void run ()
                {
                    int index = advertisementsList.indexOf (advertisement);
                    advertisementsList.remove (index);
                    fireTableRowsDeleted (index, index);
                }
            });
    }

    private class DumpAdvertisementsRunnable implements Runnable
    {
        @Override
        public void run ()
        {
            SwingUtilities.invokeLater (
                new AddAdvertisementsRunnable (
                    advertisements.getAllAdvertisements ()));
            advertisements.addAdvertisementsListener (
                AdvertisementsTableModel.this);
        }
    }

    private class AddAdvertisementsRunnable implements Runnable
    {
        private final Advertisement [] advertisements;

        public AddAdvertisementsRunnable (Advertisement [] advertisements)
        {
            if (advertisements == null)
                throw new IllegalArgumentException ("Advertisements is null");

            this.advertisements = advertisements.clone ();
        }

        @Override
        public void run ()
        {
            int advertisementsLength = advertisements.length;
            if (advertisementsLength > 0)
            {
                int index =
                    advertisementsList.size ();
                advertisementsList.addAll (
                    Arrays.asList (advertisements));
                fireTableRowsInserted (index, index + advertisementsLength - 1);
            }
        }
    }
}
